package com.ticket.system.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.analysis.WxMaSummaryTrend;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.BooleanUtils;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import com.ticket.common.utils.DateUtils;
import com.ticket.common.utils.StringUtils;
import com.ticket.system.cache.NationCache;
import com.ticket.system.domain.YanchuStatistics;
import com.ticket.system.domain.YanchuStatisticsOpera;
import com.ticket.system.domain.enu.IdTypeEnum;
import com.ticket.system.domain.vo.CustomMergeStrategy;
import com.ticket.system.domain.vo.MemberStatisticsVO;
import com.ticket.system.domain.vo.SettleGroupDetailVO;
import com.ticket.system.domain.vo.SettleGroupVO;
import com.ticket.system.mapper.YanchuOrderLogMapper;
import com.ticket.system.mapper.YanchuOrderMapper;
import com.ticket.system.mapper.YanchuStatisticsMapper;
import com.ticket.system.mapper.YanchuStatisticsOperaMapper;
import com.ticket.system.service.IYanchuStatisticsService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 【请填写功能名称】Service业务层处理
 * 
 * @author ruoyi
 * @date 2024-03-14
 */
@Service
public class YanchuStatisticsServiceImpl implements IYanchuStatisticsService 
{
    private static final Logger logger = LoggerFactory.getLogger(YanchuStatisticsServiceImpl.class);
    @Autowired
    private YanchuStatisticsMapper yanchuStatisticsMapper;

    private WxMaService wxMaService;

    @Autowired
    private YanchuStatisticsOperaMapper yanchuStatisticsOperaMapper;

    @Autowired
    private YanchuOrderLogMapper yanchuOrderLogMapper;
    @Autowired
    private YanchuOrderMapper yanchuOrderMapper;

    @Value("${tempUrl}")
    private String tempUrl;

    @Autowired
    public YanchuStatisticsServiceImpl(WxMaService wxMaService) {
        this.wxMaService = wxMaService;
    }


    /**
     * 查询【请填写功能名称】
     * 
     * @param id 【请填写功能名称】主键
     * @return 【请填写功能名称】
     */
    @Override
    public YanchuStatistics selectYanchuStatisticsById(Long id)
    {
        return yanchuStatisticsMapper.selectYanchuStatisticsById(id);
    }

    /**
     * 查询【请填写功能名称】列表
     * 
     * @param yanchuStatistics 【请填写功能名称】
     * @return 【请填写功能名称】
     */
    @Override
    public List<YanchuStatistics> selectYanchuStatisticsList(YanchuStatistics yanchuStatistics)
    {
        return yanchuStatisticsMapper.selectYanchuStatisticsList(yanchuStatistics);
    }
    @Override
    public List<Map<String,Object>> selectStatisticsList(){
        String startTime = DateUtils.getDayBefore(10);
        startTime=startTime+" 00:00:00";
        return yanchuStatisticsMapper.selectStatisticsList(startTime);
    }

    /**
     * 总用户数量
     * @return
     */
    @Override
    public Long selectStatisticsUser(){
        return yanchuStatisticsMapper.selectStatisticsUser();
    }

    /**
     * 销售额 订单统计
     * @return
     */
    public List<Map<String,Object>> staticsOrder(Integer type){
        List<Map<String,Object>> list = Lists.newArrayList();
        Map<String,Object> map = Maps.newHashMap();
        if(type <= 3){
            YanchuStatistics stat = yanchuStatisticsMapper.staticsALlOrder();
            YanchuStatistics statTicket = yanchuStatisticsMapper.staticsAllOrderTicket();
            if(type.equals(1)){//销售额
                BigDecimal sells = stat.getSellsMoney();
                map.put("value",sells);
            }else if(type.equals(2)){//总票数
                Long tickets = statTicket.getSellsTickets();
                map.put("value",tickets);
            }else if(type.equals(3)){//订单数
                Long orders = stat.getNewOrders();
                map.put("value",orders);
            }
        } else if(type.equals(4)){
            Long users = selectStatisticsUser();
            map.put("value",users);
        } else if(type.equals(5)){
            Long performCount = yanchuStatisticsMapper.selectPerformCount();
            map.put("value",performCount);
        } else if(type.equals(6)){
            Long sessionCount = yanchuStatisticsMapper.selectSessionCount();
            map.put("value",sessionCount);
        }

        list.add(map);
        return list;
    }

    /**
     * 计算百分比
     * @return
     */
    @Override
    public List<Map<String,Object>> performStatics(){
        List<Map<String,Object>> list = yanchuStatisticsMapper.performStatics();
        Long total = 0l;
        Long nums = 0l;
        BigDecimal result = null;
        for(Map<String,Object> map:list){
            total += (Long)map.get("value");
        }
        for(Map<String,Object> map:list){
            nums = (Long)map.get("value");
            result = new BigDecimal(nums).divide(new BigDecimal(total),4,BigDecimal.ROUND_HALF_UP);
            map.put("value", result.multiply(new BigDecimal(100)).intValue());
        }
        return list;
    }

    /**
     * 用户转化
     * @return
     */
    @Override
    public List<Map<String,Object>> userTransStatics(){
        Date dayBeforeSim = DateUtils.getDayBeforeSim();
        List<Map<String,Object>> listMap = Lists.newArrayList();
        try {
            logger.info("wxMaService:{}",wxMaService);
            if (!wxMaService.switchover("wx839ab821b194d9d0")) {
                throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置，请核实！", "wx839ab821b194d9d0"));
            }
            List<WxMaSummaryTrend> trends = wxMaService.getAnalysisService().getDailySummaryTrend(dayBeforeSim, dayBeforeSim);
            logger.info("trends:{}", JSONObject.toJSONString(trends));
            if (CollectionUtils.isNotEmpty(trends)) {
                Map<String,Object> maps = Maps.newHashMap();
                maps.put("type","访问人数");
                maps.put("value",trends.get(0).getVisitTotal());//累计用户数
                listMap.add(maps);
            }
        } catch (Exception e){
            logger.error("getDailySummaryTrend error",e);
            Map<String,Object> maps = Maps.newHashMap();
            maps.put("type","访问人数");
            maps.put("value",0);//累计用户数
            listMap.add(maps);
        }

        Long totalUser = yanchuStatisticsMapper.selectStatisticsUser();
        Map<String,Object> maps = Maps.newHashMap();
        maps.put("type","注册人数");
        maps.put("value",totalUser);//累计用户数
        listMap.add(maps);

        Long orderUser = yanchuStatisticsMapper.selectOrderUser();
        Map<String,Object> mapsOrder = Maps.newHashMap();
        mapsOrder.put("type","下单人数");
        mapsOrder.put("value",orderUser);//累计用户数
        listMap.add(mapsOrder);

        Long reOrderCount = yanchuStatisticsMapper.selectReOrderUser();
        Map<String,Object> mapsReOrder = Maps.newHashMap();
        mapsReOrder.put("type","复购人数");
        mapsReOrder.put("value",reOrderCount);//累计用户数
        listMap.add(mapsReOrder);
        return listMap;
    }

    /**
     * 新增【请填写功能名称】
     * 
     * @param yanchuStatistics 【请填写功能名称】
     * @return 结果
     */
    @Override
    public int insertYanchuStatistics(YanchuStatistics yanchuStatistics)
    {
        yanchuStatistics.setCreateTime(DateUtils.getNowDate());
        return yanchuStatisticsMapper.insertYanchuStatistics(yanchuStatistics);
    }

    /**
     * 修改【请填写功能名称】
     * 
     * @param yanchuStatistics 【请填写功能名称】
     * @return 结果
     */
    @Override
    public int updateYanchuStatistics(YanchuStatistics yanchuStatistics)
    {
        return yanchuStatisticsMapper.updateYanchuStatistics(yanchuStatistics);
    }

    /**
     * 批量删除【请填写功能名称】
     * 
     * @param ids 需要删除的【请填写功能名称】主键
     * @return 结果
     */
    @Override
    public int deleteYanchuStatisticsByIds(Long[] ids)
    {
        return yanchuStatisticsMapper.deleteYanchuStatisticsByIds(ids);
    }

    /**
     * 删除【请填写功能名称】信息
     * 
     * @param id 【请填写功能名称】主键
     * @return 结果
     */
    @Override
    public int deleteYanchuStatisticsById(Long id)
    {
        return yanchuStatisticsMapper.deleteYanchuStatisticsById(id);
    }

    @Override
    public void staticsPrevOrder(String date){
        String dayBefore = null;
        Date dayBeforeSim = null;
        if(StringUtils.isEmpty(date)){
            String currentDate = DateUtils.getDate();
            dayBefore = DateUtils.getDayBefore(currentDate);
            dayBeforeSim = DateUtils.getDayBeforeSim();
        }else{
            dayBefore = date;
            dayBeforeSim = DateUtils.parseDate(date);
        }
        logger.info("staticsOrder :{} dayBefore:{} dayBeforeSim:{}",date,dayBefore,dayBeforeSim);

        Long count = yanchuStatisticsMapper.selectPrevCount(dayBefore);
        if(count > 0){
            return;
        }
        YanchuStatistics staticsed = yanchuStatisticsMapper.staticsPrevOrder(dayBefore);//销售额,订单数
        YanchuStatistics staticsTicket = yanchuStatisticsMapper.staticsPrevOrderTicket(dayBefore);
        YanchuStatistics staticsedUser = yanchuStatisticsMapper.staticsPrevUsers(dayBefore);//注册用户

        staticsed.setNewMembers(staticsedUser.getNewMembers());
        staticsed.setSellsTickets(staticsTicket.getSellsTickets());
        staticsed.setStaticsDate(dayBeforeSim);
        staticsed.setCreateTime(new Date());
        yanchuStatisticsMapper.insertYanchuStatistics(staticsed);

        List<YanchuStatisticsOpera> list = yanchuStatisticsOperaMapper.staticsPrevOrderByOpera(dayBefore);//剧院统计总销售 订单数
        List<YanchuStatisticsOpera> tickets = yanchuStatisticsOperaMapper.staticsPrevOrderTicketByOpera(dayBefore);//按剧院统计票数
        Map<Long,YanchuStatisticsOpera> ticketMap = Maps.newHashMap();

        for(YanchuStatisticsOpera ticket:tickets){
            ticketMap.put(ticket.getId(),ticket);
        }
        for(YanchuStatisticsOpera stat:list){
            stat.setSellsTickets(ticketMap.get(stat.getId()).getSellsTickets());
            stat.setCreateTime(new Date());
            stat.setStaticsDate(dayBeforeSim);
        }
        if(CollectionUtils.isNotEmpty(list)){
            yanchuStatisticsOperaMapper.insertBatchOpera(list);
        }
        yanchuOrderLogMapper.deletePastedOrderLog();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int initPrevOrder(){
        String currentDate = DateUtils.getDate();
        String dayBefore = DateUtils.getDayBefore(currentDate);
        Long count = yanchuStatisticsMapper.selectPrevCount(dayBefore);
        logger.info("dayBefore :{} count:{}",dayBefore,count);
        if(count > 0){
            return 0;
        }
        List<YanchuStatistics> staticsed = yanchuStatisticsMapper.staticsAllPrevOrder();//销售额,订单数
        List<YanchuStatistics> staticsTicket = yanchuStatisticsMapper.staticsAllPrevOrderTicket();//销售票数
        List<YanchuStatistics> staticsedUser = yanchuStatisticsMapper.staticsAllPrevUsers();//注册用户
        Map<String,Long> ticketMap = Maps.newHashMap();
        Map<String,Long> userMap = Maps.newHashMap();

        for(YanchuStatistics ticket:staticsTicket){
            ticketMap.put(ticket.getStaticsDateStr(),ticket.getSellsTickets());
        }
        for(YanchuStatistics ticket:staticsedUser){
            userMap.put(ticket.getStaticsDateStr(),ticket.getNewMembers());
        }
        for(YanchuStatistics statistics:staticsed){
            statistics.setNewMembers(userMap.get(statistics.getStaticsDateStr()));
            userMap.remove(statistics.getStaticsDateStr());
            statistics.setSellsTickets(ticketMap.get(statistics.getStaticsDateStr()));
            statistics.setStaticsDate(DateUtils.parseDate(statistics.getStaticsDateStr()));
        }
        if(!userMap.isEmpty()){
            YanchuStatistics yanchuStatistics = null;
            for(Map.Entry<String,Long> iter:userMap.entrySet()){
                yanchuStatistics = new YanchuStatistics();
                yanchuStatistics.setNewMembers(userMap.get(iter.getValue()));
                yanchuStatistics.setStaticsDate(DateUtils.parseDate(iter.getKey()));
                staticsed.add(yanchuStatistics);
            }
        }

        yanchuStatisticsMapper.insertBatch(staticsed);

        List<YanchuStatisticsOpera> listOpera = yanchuStatisticsOperaMapper.staticsAllPrevOrderByOpera();//剧院统计总销售 订单数
        List<YanchuStatisticsOpera> ticketsOpera = yanchuStatisticsOperaMapper.staticsAllPrevOrderTicketByOpera();//按剧院统计票数
        Table<String, Long, Long> tabs = HashBasedTable.create();
        for(YanchuStatisticsOpera ticket:ticketsOpera){
            tabs.put(ticket.getStaticsDateStr(),ticket.getOperaId(),ticket.getSellsTickets());
        }
        for(YanchuStatisticsOpera stat:listOpera){
            stat.setSellsTickets(tabs.get(stat.getStaticsDateStr(),stat.getOperaId()));
            stat.setStaticsDate(DateUtils.parseDate(stat.getStaticsDateStr()));
        }
        return yanchuStatisticsOperaMapper.insertBatchOpera(listOpera);
    }

    /**
     * 场次按演出统计
     */
    @Override
    public List<Map<String,Object>> listSessionByPerform(String sessionIds){
        Map<String,Object> mapBackend = Maps.newHashMap();
        Map<String,Object> mapProg = Maps.newHashMap();
        List<Map<String,Object>> listMap = yanchuStatisticsMapper.listSessionByPerform(sessionIds);
        List<Map<String,Object>> listProgMap = yanchuStatisticsMapper.listSessionOrderByPerformFromProg(sessionIds);
        List<Map<String,Object>> listOrderBackendMap = yanchuStatisticsMapper.listSessionOrderByPerformFromBackend(sessionIds);
        if(CollectionUtils.isNotEmpty(listProgMap)){
            for(Map<String,Object> orderMap:listProgMap){
                mapProg.put(orderMap.get("performId")+"", orderMap);
            }
        }

        if(CollectionUtils.isNotEmpty(listOrderBackendMap)) {
            for (Map<String, Object> orderMap : listOrderBackendMap) {
                mapBackend.put(orderMap.get("performId") + "", orderMap);
            }
        }
        Long seatNumsProg = 0l;
        BigDecimal sellNumsTotal = null,seatNumsExpect=null,countTicketRes=null;
        Map<String, Object> orderMap = null;
        for(int i=0;i<listMap.size();i++){
            orderMap = listMap.get(i);
            if(null == orderMap){
                continue;
            }
            orderMap.put("index",i+1);
            if(null != mapBackend.get(orderMap.get("performId")+"")){
                orderMap.putAll((Map<String,Object>)mapBackend.get(orderMap.get("performId")+""));
            }else{
                orderMap.put("ticketNumsTuan",0l);
                orderMap.put("yingfuMoneyTuan",BigDecimal.ZERO);
                orderMap.put("discountAmtTuan",BigDecimal.ZERO);
                orderMap.put("totalMoneyTuan",BigDecimal.ZERO);
                orderMap.put("shiShouTuan",BigDecimal.ZERO);
                orderMap.put("ticketNumsOther",0l);
                orderMap.put("yingfuMoneyOther",BigDecimal.ZERO);
                orderMap.put("discountAmtOther",BigDecimal.ZERO);
                orderMap.put("totalMoneyOther",BigDecimal.ZERO);
                orderMap.put("shiShouOther",BigDecimal.ZERO);
                orderMap.put("ticketNumsZeng",0l);
                orderMap.put("yingfuMoneyZeng",BigDecimal.ZERO);
                orderMap.put("ticketNumsWork",0l);
                orderMap.put("yingfuMoneyWork",BigDecimal.ZERO);
            }
            if(null != mapProg.get(orderMap.get("performId")+"")){
                orderMap.putAll((Map<String,Object>)mapProg.get(orderMap.get("performId")+""));
            }else{
                orderMap.put("seatNumsProg",0l);
                orderMap.put("yingfuMoneyProg",BigDecimal.ZERO);
                orderMap.put("discountAmtProg",BigDecimal.ZERO);
                orderMap.put("totalMoneyProg",BigDecimal.ZERO);
            }

            //售出率：【售出票数÷（总规划票数-预留票数）】×100%
            seatNumsProg = (Long)orderMap.get("seatNumsProg");
            sellNumsTotal = (BigDecimal)orderMap.get("sellNumsTotal");
            seatNumsExpect = (BigDecimal)orderMap.get("seatNumsExpect");
            countTicketRes = (BigDecimal)orderMap.get("countTicketRes");

            if(seatNumsExpect.compareTo(countTicketRes)>0){
                BigDecimal sellsPercent = BigDecimal.valueOf(seatNumsProg).divide(seatNumsExpect.subtract(countTicketRes),4,BigDecimal.ROUND_HALF_UP);
                orderMap.put("sellsPercent",sellsPercent.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
            }else{
                orderMap.put("sellsPercent","0%");
            }
            if(seatNumsExpect.compareTo(BigDecimal.ZERO)>0){
                //总体上座率：【总出票数（含普通票、团购票、赠票、工作票、其他票）÷总规划票数】×100%
                BigDecimal seatPercent = sellNumsTotal.divide(seatNumsExpect,4,BigDecimal.ROUND_HALF_UP);
                orderMap.put("seatPercent",seatPercent.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
            }else{
                orderMap.put("seatPercent","0%");
            }
        }
        return listMap;
    }

    /**
     * 场次按演出统计汇总
     * @param sessionIds
     * @return
     */
    @Override
    public Map<String,Object> listSessionCollectByPerform(String sessionIds){
        List<Map<String,Object>> sessionMaps = yanchuStatisticsMapper.listSessionCollectByPerform(sessionIds);
        List<Map<String,Object>> progMap = yanchuStatisticsMapper.listSessionOrderCollectByPerformFromProg(sessionIds);
        List<Map<String,Object>> backendMap = yanchuStatisticsMapper.listSessionCollectOrderByPerformFromBackend(sessionIds);
        if(CollectionUtils.isNotEmpty(progMap)){
            sessionMaps.get(0).putAll(progMap.get(0));
        }
        if(CollectionUtils.isNotEmpty(backendMap)){
            sessionMaps.get(0).putAll(backendMap.get(0));
        }
        //售出率：【售出票数÷（总规划票数-预留票数）】×100%
        Long seatNumsProgCollect = (Long)progMap.get(0).get("seatNumsProgCollect");
        BigDecimal sellNumsTotal = (BigDecimal)sessionMaps.get(0).get("sellNumsTotalCollect");
        BigDecimal seatNumsExpectCollect = (BigDecimal)sessionMaps.get(0).get("seatNumsExpectCollect");
        BigDecimal countTicketResCollect = (BigDecimal)sessionMaps.get(0).get("countTicketResCollect");
        sessionMaps.get(0).put("sessioCountCollect",sessionIds.split(",").length);
        if(seatNumsExpectCollect.compareTo(countTicketResCollect) > 0){
            BigDecimal sellsPercentCollect = BigDecimal.valueOf(seatNumsProgCollect).divide(seatNumsExpectCollect.subtract(countTicketResCollect),4,BigDecimal.ROUND_HALF_UP);
            sessionMaps.get(0).put("sellsPercentCollect",sellsPercentCollect.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
        }else{
            sessionMaps.get(0).put("sellsPercentCollect",0);
        }
        if(seatNumsExpectCollect.compareTo(BigDecimal.ZERO) > 0){
            //总体上座率：【总出票数（含普通票、团购票、赠票、工作票、其他票）÷总规划票数】×100%
            BigDecimal seatPercent = sellNumsTotal.divide(seatNumsExpectCollect,4,BigDecimal.ROUND_HALF_UP);
            sessionMaps.get(0).put("seatPercentCollect",seatPercent.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
        }else{
            sessionMaps.get(0).put("seatPercentCollect",0);
        }
        return sessionMaps.get(0);
    }
    /**
     * 场次按场次统计
     * @param sessionIds
     * @return
     */
    @Override
    public List<Map<String,Object>> listSessionBySession(String sessionIds){
        Map<String,Object> mapBackend = Maps.newHashMap();
        Map<String,Object> mapProg = Maps.newHashMap();
        List<Map<String,Object>> listMap = yanchuStatisticsMapper.listSessionBySession(sessionIds);
        List<Map<String,Object>> listProgMap = yanchuStatisticsMapper.listSessionOrderBySessionFromProg(sessionIds);
        List<Map<String,Object>> listOrderBackendMap = yanchuStatisticsMapper.listSessionOrderBySessionFromBackend(sessionIds);
        if(CollectionUtils.isNotEmpty(listProgMap)){
            for(Map<String,Object> orderMap:listProgMap){
                mapProg.put(orderMap.get("sessionId")+"", orderMap);
            }
        }

        if(CollectionUtils.isNotEmpty(listOrderBackendMap)) {
            for (Map<String, Object> orderMap : listOrderBackendMap) {
                mapBackend.put(orderMap.get("sessionId") + "", orderMap);
            }
        }
        Long seatNumsProg = 0l;
        BigDecimal sellNumsTotal=null,seatNumsExpect=null,countTicketRes=null;
        Map<String, Object> orderMap = null;
        for(int i=0;i<listMap.size();i++){
            orderMap = listMap.get(i);
            orderMap.put("index",i+1);
            if(null != mapBackend.get(orderMap.get("sessionId")+"")){
                orderMap.putAll((Map<String,Object>)mapBackend.get(orderMap.get("sessionId")+""));
            }else{
                orderMap.put("ticketNumsTuan",0l);
                orderMap.put("yingfuMoneyTuan",BigDecimal.ZERO);
                orderMap.put("discountAmtTuan",BigDecimal.ZERO);
                orderMap.put("totalMoneyTuan",BigDecimal.ZERO);
                orderMap.put("shiShouTuan",BigDecimal.ZERO);
                orderMap.put("ticketNumsOther",0l);
                orderMap.put("yingfuMoneyOther",BigDecimal.ZERO);
                orderMap.put("discountAmtOther",BigDecimal.ZERO);
                orderMap.put("totalMoneyOther",BigDecimal.ZERO);
                orderMap.put("shiShouOther",BigDecimal.ZERO);
                orderMap.put("ticketNumsZeng",0l);
                orderMap.put("yingfuMoneyZeng",BigDecimal.ZERO);
                orderMap.put("ticketNumsWork",0l);
                orderMap.put("yingfuMoneyWork",BigDecimal.ZERO);
            }
            if(null != mapProg.get(orderMap.get("sessionId")+"")){
                orderMap.putAll((Map<String,Object>)mapProg.get(orderMap.get("sessionId")+""));
            }else{
                orderMap.put("seatNumsProg",0l);
                orderMap.put("yingfuMoneyProg",BigDecimal.ZERO);
                orderMap.put("discountAmtProg",BigDecimal.ZERO);
                orderMap.put("totalMoneyProg",BigDecimal.ZERO);
            }

            //售出率：【售出票数÷（总规划票数-预留票数）】×100%
            seatNumsProg = (Long)orderMap.get("seatNumsProg");
            sellNumsTotal = (BigDecimal)orderMap.get("sellNumsTotal");
            seatNumsExpect = (BigDecimal)orderMap.get("seatNumsExpect");
            countTicketRes = (BigDecimal)orderMap.get("countTicketRes");

            if(seatNumsExpect.compareTo(countTicketRes)>0){
                BigDecimal sellsPercent = BigDecimal.valueOf(seatNumsProg).divide(seatNumsExpect.subtract(countTicketRes),4,BigDecimal.ROUND_HALF_UP);
                orderMap.put("sellsPercent",sellsPercent.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
            }else{
                orderMap.put("sellsPercent",0);
            }
            if(seatNumsExpect.compareTo(BigDecimal.ZERO)>0){
                //总体上座率：【总出票数（含普通票、团购票、赠票、工作票、其他票）÷总规划票数】×100%
                BigDecimal seatPercent = sellNumsTotal.divide(seatNumsExpect,4,BigDecimal.ROUND_HALF_UP);
                orderMap.put("seatPercent",seatPercent.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
            }else{
                orderMap.put("seatPercent",0);
            }
        }
        return listMap;
    }
    /**
     * 场次按场次统计汇总
     * @param sessionIds
     * @return
     */
    @Override
    public Map<String,Object> listSessionCollectBySession(String sessionIds){
        List<Map<String,Object>> sessionMap = yanchuStatisticsMapper.listSessionCollectByPerform(sessionIds);
        List<Map<String,Object>> progMap = yanchuStatisticsMapper.listSessionOrderCollectByPerformFromProg(sessionIds);
        List<Map<String,Object>> backendMap = yanchuStatisticsMapper.listSessionCollectOrderByPerformFromBackend(sessionIds);
        if(null != progMap){
            sessionMap.get(0).putAll(progMap.get(0));
        }
        if(null != backendMap){
            sessionMap.get(0).putAll(backendMap.get(0));
        }
        //售出率：【售出票数÷（总规划票数-预留票数）】×100%
        Long seatNumsProgCollect = (Long)progMap.get(0).get("seatNumsProgCollect");
        BigDecimal sellNumsTotal = (BigDecimal)sessionMap.get(0).get("sellNumsTotalCollect");
        BigDecimal seatNumsExpectCollect = (BigDecimal)sessionMap.get(0).get("seatNumsExpectCollect");
        BigDecimal countTicketResCollect = (BigDecimal)sessionMap.get(0).get("countTicketResCollect");
        sessionMap.get(0).put("sessioCountCollect",sessionIds.split(",").length);
        if(seatNumsExpectCollect.compareTo(countTicketResCollect)>0){
            BigDecimal sellsPercentCollect = BigDecimal.valueOf(seatNumsProgCollect).divide(seatNumsExpectCollect.subtract(countTicketResCollect),4,BigDecimal.ROUND_HALF_UP);
            sessionMap.get(0).put("sellsPercentCollect",sellsPercentCollect.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
        }else{
            sessionMap.get(0).put("sellsPercentCollect","0%");
        }

        if(seatNumsExpectCollect.compareTo(BigDecimal.ZERO)>0){
            //总体上座率：【总出票数（含普通票、团购票、赠票、工作票、其他票）÷总规划票数】×100%
            BigDecimal seatPercentCollect = sellNumsTotal.divide(seatNumsExpectCollect,4,BigDecimal.ROUND_HALF_UP);
            sessionMap.get(0).put("seatPercentCollect",seatPercentCollect.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
        }else{
            sessionMap.get(0).put("seatPercentCollect","0%");
        }
        return sessionMap.get(0);
    }

    @Override
    public void exportPerformStatistics(HttpServletResponse response, String sessionIds) {
        List<Map<String,Object>> list = listSessionByPerform(sessionIds);
        Map<String,Object> mapCollect = listSessionCollectByPerform(sessionIds);

        // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
        String templateFileName = tempUrl + "performStaticsExportTemp.xlsx";
        String fileName = "performStaticsExport"+System.currentTimeMillis()+".xlsx";
//        String fileName = "C:/Users/lovepig/Desktop/" + "导出订单列表" + System.currentTimeMillis() + ".xlsx";
        try {
            //替换成自己的数据
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            ExcelWriterBuilder write = EasyExcel.write(response.getOutputStream());

            //只有一个直接写 >1的合并 分开写入
            try (ExcelWriter excelWriter = write.withTemplate(templateFileName).build()){
                WriteSheet writeSheet = EasyExcel.writerSheet().build();
                // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。
                // 默认 是false，会直接使用下一行，如果没有则创建。
                // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
                // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
                // 如果数据量大 list不是最后一行 参照下一个
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                excelWriter.fill(list, fillConfig, writeSheet);
                Map<String, Object> map = MapUtils.newHashMap();
                map.put("sessioCountCollect",sessionIds.split(",").length);
                map.put("title", DateUtils.getTime());
                map.putAll(mapCollect);
                excelWriter.fill(map, writeSheet);
            }
        } catch (Exception e) {
            logger.error("exportPerformStatistics",e);
        }
    }

    @Override
    public void exportSessionStatistics(HttpServletResponse response, String sessionIds) {
        List<Map<String,Object>> list = listSessionBySession(sessionIds);
        Map<String,Object> mapCollect = listSessionCollectBySession(sessionIds);

        // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
        String templateFileName = tempUrl + "sessionStaticsExportTemp.xlsx";
        String fileName = "sessionStaticsExport"+System.currentTimeMillis()+".xlsx";
//        String fileName = "C:/Users/lovepig/Desktop/" + "导出订单列表" + System.currentTimeMillis() + ".xlsx";
        try {
            //替换成自己的数据
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            ExcelWriterBuilder write = EasyExcel.write(response.getOutputStream());

            //只有一个直接写 >1的合并 分开写入
            try (ExcelWriter excelWriter = write.withTemplate(templateFileName).build()){
                WriteSheet writeSheet = EasyExcel.writerSheet().build();
                // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。
                // 默认 是false，会直接使用下一行，如果没有则创建。
                // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
                // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
                // 如果数据量大 list不是最后一行 参照下一个
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                excelWriter.fill(list, fillConfig, writeSheet);
                Map<String, Object> map = MapUtils.newHashMap();
                map.put("title", DateUtils.getTime());
                map.putAll(mapCollect);
                excelWriter.fill(map, writeSheet);
            }
        } catch (Exception e) {
            logger.error("exportPerformStatistics",e);
        }
    }

    @Override
    public void exportListDetailsStatistics(HttpServletResponse response, String sessionIds) {
        List<Map<String,Object>> list = listDetailsBySession(sessionIds);
        Map<String,Object> performMap = list.get(0);
        List<Map<String,Object>> sessionList = (List<Map<String,Object>>)performMap.get("sessionList");
        performMap.remove("sessionList");
        Map<String,Object> performCollect = (Map<String,Object>)performMap.get("performCollect");
        // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
        String templateFileName = tempUrl + "listDetailexportTemp.xlsx";
        String fileName = "listDetailexport"+System.currentTimeMillis()+".xlsx";
//        String fileName = "C:/Users/lovepig/Desktop/" + "导出订单列表" + System.currentTimeMillis() + ".xlsx";
        try {
            //替换成自己的数据
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            ExcelWriterBuilder write = EasyExcel.write(response.getOutputStream());

            //只有一个直接写 >1的合并 分开写入
            try (ExcelWriter excelWriter = write.withTemplate(templateFileName).build()){
                WriteSheet writeSheet = EasyExcel.writerSheet().needHead(Boolean.FALSE).registerWriteHandler(new CellWriteHandler() {
                    @Override
                    public void afterCellDispose(CellWriteHandlerContext context) {
                        WriteCellData<?> cellData = context.getFirstCellData();
                        WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
                        WriteFont writeFont = new WriteFont();
                        writeFont.setFontHeightInPoints((short) 10);
                        writeFont.setFontName("宋体");
                        writeCellStyle.setWriteFont(writeFont);
                        Cell cell = context.getCell();
//                        if(cell.getCellType().equals(CellType.NUMERIC)){
//                            logger.info("val:{} {} {} {}",cell.getNumericCellValue(),cell.getColumnIndex(),cell.getRow(),cell.getRowIndex());
//                        }else{
//                            logger.info("val:{} {} {} {}",cell.getStringCellValue(),cell.getColumnIndex(),cell.getRow(),cell.getRowIndex());
//                        }
                    }
                }).build();
                // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。
                // 默认 是false，会直接使用下一行，如果没有则创建。
                // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
                // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
                // 如果数据量大 list不是最后一行 参照下一个
                Map<String, Object> map = MapUtils.newHashMap();
                map.putAll(performMap);
                map.put("exportTime", DateUtils.getTime());
                excelWriter.fill(map, writeSheet);
                final String[] title = new String[1];
                Map<String,Object> map1 = null;
                for(int i = 0;i<sessionList.size();i++){
                    map1 = sessionList.get(i);
                    title[0] = "场次"+(i+1)+":"+(String)map1.get("playTime")+"("+map1.get("status")+")";
                    WriteTable writeTable0 = EasyExcel.writerTable(i).needHead(Boolean.TRUE).head(head(title[0]))
                            .registerWriteHandler(new CellWriteHandler() {
                        @Override
                        public void afterCellDispose(CellWriteHandlerContext context) {
                            // 当前事件会在 数据设置到poi的cell里面才会回调
                            // 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true

                            // 第一个单元格
                            // 只要不是头 一定会有数据 当然fill的情况 可能要context.getCellDataList() ,这个需要看模板，因为一个单元格会有多个 WriteCellData
                            WriteCellData<?> cellData = context.getFirstCellData();
                            // 这里需要去cellData 获取样式
                            // 很重要的一个原因是 WriteCellStyle 和 dataFormatData绑定的 简单的说 比如你加了 DateTimeFormat
                            // ，已经将writeCellStyle里面的dataFormatData 改了 如果你自己new了一个WriteCellStyle，可能注解的样式就失效了
                            // 然后 getOrCreateStyle 用于返回一个样式，如果为空，则创建一个后返回
                            WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();
                                writeCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
                            WriteFont writeFont = new WriteFont();
                            writeFont.setFontHeightInPoints((short) 10);
                            writeFont.setFontName("宋体");
                            writeCellStyle.setBorderRight(BorderStyle.THIN);
                            writeCellStyle.setBorderBottom(BorderStyle.THIN);
                            writeCellStyle.setBorderTop(BorderStyle.THIN);
                            writeCellStyle.setBorderLeft(BorderStyle.THIN);
                            writeCellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
                            writeCellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
                            writeCellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
                            writeCellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
                            // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND
                            writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);

                            // 这样样式就设置好了 后面有个FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到 cell里面去 所以可以不用管了
                            if (BooleanUtils.isTrue(context.getHead())) {
                                writeFont.setBold(true);
                                writeFont.setFontHeightInPoints((short) 14);
                                writeCellStyle.setFillBackgroundColor(IndexedColors.WHITE.getIndex());
                                Cell cell = context.getCell();
                                if(cell.getStringCellValue().equals(title[0])){
                                    writeCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
                                }
                                logger.info("val:{} {} {} {}",cell.getStringCellValue(),cell.getColumnIndex(),cell.getRow(),cell.getRowIndex());
                            }else{
                                writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
                            }
                            writeCellStyle.setWriteFont(writeFont);
                        }
                    }).build();

                    excelWriter.write(dataList((List<Map<String,Object>>)map1.get("pricesList")), writeSheet, writeTable0);
                    if(i == sessionList.size()-1){
                        List<Map<String,Object>> listCollect = Lists.newArrayList();
                        listCollect.add(performCollect);
                        excelWriter.write(dataList(listCollect), writeSheet, writeTable0);
                    }
                }

            }
        } catch (Exception e) {
            logger.error("exportPerformStatistics",e);
        }
    }

    private List<List<Object>> dataList(List<Map<String,Object>> pricesList) {
        if(CollectionUtils.isEmpty(pricesList)){
            return null;
        }
        List<List<Object>> list = ListUtils.newArrayList();
        for (Map<String,Object> map: pricesList) {
            List<Object> data = ListUtils.newArrayList();
            data.add(map.get("title"));
            data.add(map.get("seatNumsExpect"));
            data.add(map.get("totalMoneyExpect"));
            data.add(map.get("countTicketRes"));
            data.add(map.get("totalResMoney"));
            data.add(map.get("seatNumsProg"));
            data.add(map.get("yingfuMoneyProg"));
            data.add(map.get("discountAmtProg"));
            data.add(map.get("totalMoneyProg"));
            data.add(map.get("totalMoneyProg"));
            data.add(map.get("sellsPercent"));
            data.add(map.get("ticketNumsTuan"));
            data.add(map.get("yingfuMoneyTuan"));
            data.add(map.get("discountAmtTuan"));
            data.add(map.get("totalMoneyTuan"));
            data.add(map.get("shiShouTuan"));
            data.add(map.get("ticketNumsOther"));
            data.add(map.get("yingfuMoneyOther"));
            data.add(map.get("discountAmtOther"));
            data.add(map.get("totalMoneyOther"));
            data.add(map.get("shiShouOther"));
            data.add(map.get("ticketNumsZeng"));
            data.add(map.get("yingfuMoneyZeng"));
            data.add(map.get("ticketNumsWork"));
            data.add(map.get("yingfuMoneyWork"));
            data.add(map.get("countLeftTicket"));
            data.add(map.get("totalLeftMoney"));
            data.add(map.get("seatPercent"));
            list.add(data);
        }
        return list;
    }

    private List<List<String>> head(String title) {
        List<List<String>> headTitles = Lists.newArrayList();
        headTitles.add(Lists.newArrayList(title,"票档", "票档", "票档"));
        headTitles.add(Lists.newArrayList(title,"规划票房", "规划票房", "票数（张）"));
        headTitles.add(Lists.newArrayList(title,"规划票房", "规划票房", "金额（元）"));
        headTitles.add(Lists.newArrayList(title,"预留票房", "预留票房", "票数（张）"));
        headTitles.add(Lists.newArrayList(title,"预留票房", "预留票房", "金额（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "普通票销售", "票数（张）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "普通票销售", "原价（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "普通票销售", "优惠（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "普通票销售", "应收（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "普通票销售", "实收（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "普通票销售", "售出率"));

        headTitles.add(Lists.newArrayList(title,"销售票房", "团购票销售", "票数（张）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "团购票销售", "原价（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "团购票销售", "优惠（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "团购票销售", "应收（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "团购票销售", "实收（元）"));

        headTitles.add(Lists.newArrayList(title,"销售票房", "其他类型门票出票", "票数（张）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "其他类型门票出票", "原价（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "其他类型门票出票", "优惠（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "其他类型门票出票", "应收（元）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "其他类型门票出票", "实收（元）"));

        headTitles.add(Lists.newArrayList(title,"销售票房", "工作票出票", "票数（张）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "工作票出票", "票面价（元）"));

        headTitles.add(Lists.newArrayList(title,"销售票房", "赠票出票", "票数（张）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "赠票出票", "票面价（元）"));

        headTitles.add(Lists.newArrayList(title,"销售票房", "剩余票房", "剩余票数（张）"));
        headTitles.add(Lists.newArrayList(title,"销售票房", "剩余票房", "剩余金额（元）"));

        headTitles.add(Lists.newArrayList(title,"销售票房", "总体上座率", "总体上座率"));
        return headTitles;
    }

    /**
     * 场次明细查询
     * @param sessionIds
     * @return
     */
    @Override
    public List<Map<String,Object>> listDetailsBySession(String sessionIds){
        Map<String,Object> mapBackend = Maps.newHashMap();
        Map<String,Object> mapProg = Maps.newHashMap();
        Map<String,List<Map<String,Object>>> mapSessions = Maps.newHashMap();
        Map<String,List<Map<String,Object>>> mapSessionPrices = Maps.newHashMap();

        List<Map<String,Object>> listPerformsMap = yanchuStatisticsMapper.listDetailsByPerforms(sessionIds);
        List<Map<String,Object>> listSessionsMap = yanchuStatisticsMapper.listDetailsBySessions(sessionIds);

        List<Map<String,Object>> listTicketPriceMap = yanchuStatisticsMapper.listDetailsByTicketPrices(sessionIds);
        List<Map<String,Object>> listPriceProgMap = yanchuStatisticsMapper.listDetailsByTicketPricesFromProg(sessionIds);
        List<Map<String,Object>> listPriceBackendMap = yanchuStatisticsMapper.listDetailsByTicketPriceFromBackend(sessionIds);

        for(Map<String,Object> map:listSessionsMap){
            if(null == mapSessions.get(map.get("performId")+"")){
                mapSessions.put(map.get("performId")+"",Lists.newArrayList());
            }
            mapSessions.get(map.get("performId")+"").add(map);
        }
        //保存演出-场次
        for(Map<String,Object> map:listPerformsMap){
            map.put("sessionList",mapSessions.get(map.get("performId")+""));
        }
        //小程序售票
        if(CollectionUtils.isNotEmpty(listPriceProgMap)) {
            for (Map<String, Object> orderMap : listPriceProgMap) {
                mapProg.put(orderMap.get("priceId") + "", orderMap);
            }
        }
        //后台出票
        if(CollectionUtils.isNotEmpty(listPriceBackendMap)) {
            for (Map<String, Object> orderMap : listPriceBackendMap) {
                mapBackend.put(orderMap.get("priceId") + "", orderMap);
            }
        }

        Long seatNumsProg = 0l,seatNumsExpect=null;
        BigDecimal sellNumsTotal=null,countTicketRes=null;
        //票档 规划票房
        for(Map<String,Object> ticketPriceMap:listTicketPriceMap){
            //拼接小程序售票
            if(null != mapProg.get(ticketPriceMap.get("priceId")+"")){
                ticketPriceMap.putAll((Map<String,Object>)mapProg.get(ticketPriceMap.get("priceId")+""));
            }else{
                ticketPriceMap.put("seatNumsProg",0l);
                ticketPriceMap.put("yingfuMoneyProg",BigDecimal.ZERO);
                ticketPriceMap.put("discountAmtProg",BigDecimal.ZERO);
                ticketPriceMap.put("totalMoneyProg",BigDecimal.ZERO);
            }
            //拼接后台出票
            if(null != mapBackend.get(ticketPriceMap.get("priceId")+"")){
                ticketPriceMap.putAll((Map<String,Object>)mapBackend.get(ticketPriceMap.get("priceId")+""));
            }else{
                ticketPriceMap.put("ticketNumsTuan",BigDecimal.ZERO);
                ticketPriceMap.put("yingfuMoneyTuan",BigDecimal.ZERO);
                ticketPriceMap.put("discountAmtTuan",BigDecimal.ZERO);
                ticketPriceMap.put("totalMoneyTuan",BigDecimal.ZERO);
                ticketPriceMap.put("shiShouTuan",BigDecimal.ZERO);
                ticketPriceMap.put("ticketNumsOther",BigDecimal.ZERO);
                ticketPriceMap.put("yingfuMoneyOther",BigDecimal.ZERO);
                ticketPriceMap.put("discountAmtOther",BigDecimal.ZERO);
                ticketPriceMap.put("totalMoneyOther",BigDecimal.ZERO);
                ticketPriceMap.put("shiShouOther",BigDecimal.ZERO);
                ticketPriceMap.put("ticketNumsZeng",BigDecimal.ZERO);
                ticketPriceMap.put("yingfuMoneyZeng",BigDecimal.ZERO);
                ticketPriceMap.put("ticketNumsWork",BigDecimal.ZERO);
                ticketPriceMap.put("yingfuMoneyWork",BigDecimal.ZERO);
            }
            //场次编码-票档列表
            if(null == mapSessionPrices.get(ticketPriceMap.get("sessionId")+"")){
                mapSessionPrices.put(ticketPriceMap.get("sessionId")+"",Lists.newArrayList());
            }
            mapSessionPrices.get(ticketPriceMap.get("sessionId")+"").add(ticketPriceMap);

            //售出率：【售出票数÷（总规划票数-预留票数）】×100%
            seatNumsProg = (Long)ticketPriceMap.get("seatNumsProg");
//            sellNumsTotal = (BigDecimal)ticketPriceMap.get("sellNumsTotal");
            seatNumsExpect = (Long)ticketPriceMap.get("seatNumsExpect");
            countTicketRes = (BigDecimal)ticketPriceMap.get("countTicketRes");

            if(BigDecimal.valueOf(seatNumsExpect).compareTo(countTicketRes) > 0){
                BigDecimal sellsPercent = BigDecimal.valueOf(seatNumsProg).divide(BigDecimal.valueOf(seatNumsExpect).subtract(countTicketRes),4,BigDecimal.ROUND_HALF_UP);
                ticketPriceMap.put("sellsPercent",sellsPercent.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
            }else{
                ticketPriceMap.put("sellsPercent","0%");
            }
            if(seatNumsExpect > 0){
                BigDecimal numsTotal = BigDecimal.valueOf(seatNumsProg).add((BigDecimal)ticketPriceMap.get("ticketNumsTuan"))
                        .add((BigDecimal)ticketPriceMap.get("ticketNumsOther"))
                        .add((BigDecimal)ticketPriceMap.get("ticketNumsZeng"))
                        .add((BigDecimal)ticketPriceMap.get("ticketNumsWork"));
                //总体上座率：【总出票数（含普通票、团购票、赠票、工作票、其他票）÷总规划票数】×100%
                BigDecimal seatPercent = numsTotal.divide(BigDecimal.valueOf(seatNumsExpect),4,BigDecimal.ROUND_HALF_UP);
                ticketPriceMap.put("seatPercent",seatPercent.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
            }else{
                ticketPriceMap.put("seatPercent","0%");
            }
        }
        //场次编码-汇总
        Map<String,Map<String,Object>> collectMap = detailsCollectMapMake(sessionIds);
        for(Map.Entry<String,List<Map<String,Object>>> entry:mapSessionPrices.entrySet()){
            if(null!=collectMap.get(entry.getKey())){
                entry.getValue().add(collectMap.get(entry.getKey()));
            }
        }
        Map<String,Map<String,Object>> collectPerformMap = detailsCollectMapByPerformMake(sessionIds);
        for(Map<String,Object> entry:listPerformsMap){
            entry.put("performCollect",collectPerformMap.get(entry.get("performId")+""));
        }

        for(Map<String,Object> session:listSessionsMap){
            session.put("pricesList",mapSessionPrices.get(session.get("sessionId")+""));
        }

        return listPerformsMap;
    }

    /**
     * 生成场次编码 场次统计映射
     * @return
     */
    private Map<String,Map<String,Object>> detailsCollectMapMake(String sessionIds){
        Map<String,Map<String,Object>> mapSessionProgCollect = Maps.newHashMap();
        Map<String,Map<String,Object>> mapSessionBackendCollect = Maps.newHashMap();
        Map<String,Map<String,Object>> mapSessionMapCollect = Maps.newHashMap();
        List<Map<String,Object>> listSessionCollectMap = yanchuStatisticsMapper.listDetailsByTicketPricesCollect(sessionIds);
        List<Map<String,Object>> listDetailsCollectFromProg = yanchuStatisticsMapper.listDetailsCollectFromProg(sessionIds);
        List<Map<String,Object>> listDetailsCollectFromBackend = yanchuStatisticsMapper.listDetailsCollectFromBackend(sessionIds);

        if(CollectionUtils.isNotEmpty(listDetailsCollectFromProg)){
            for(Map<String,Object> map:listDetailsCollectFromProg){
                mapSessionProgCollect.put(map.get("sessionId")+"",map);
            }
        }
        if(CollectionUtils.isNotEmpty(listDetailsCollectFromBackend)){
            for(Map<String,Object> map:listDetailsCollectFromBackend){
                mapSessionBackendCollect.put(map.get("sessionId")+"",map);
            }
        }
        for(Map<String,Object> map:listSessionCollectMap){
            if(null != mapSessionProgCollect.get(map.get("sessionId")+"")){
                map.putAll(mapSessionProgCollect.get(map.get("sessionId")+""));
            }else{
                map.put("seatNumsProg",0l);
                map.put("yingfuMoneyProg",BigDecimal.ZERO);
                map.put("discountAmtProg",BigDecimal.ZERO);
                map.put("totalMoneyProg",BigDecimal.ZERO);
            }

            if(null != mapSessionBackendCollect.get(map.get("sessionId")+"")){
                map.putAll(mapSessionBackendCollect.get(map.get("sessionId")+""));
            }else{
                map.put("ticketNumsTuan",BigDecimal.ZERO);
                map.put("yingfuMoneyTuan",BigDecimal.ZERO);
                map.put("discountAmtTuan",BigDecimal.ZERO);
                map.put("totalMoneyTuan",BigDecimal.ZERO);
                map.put("shiShouTuan",BigDecimal.ZERO);
                map.put("ticketNumsOther",BigDecimal.ZERO);
                map.put("yingfuMoneyOther",BigDecimal.ZERO);
                map.put("discountAmtOther",BigDecimal.ZERO);
                map.put("totalMoneyOther",BigDecimal.ZERO);
                map.put("shiShouOther",BigDecimal.ZERO);
                map.put("ticketNumsZeng",BigDecimal.ZERO);
                map.put("yingfuMoneyZeng",BigDecimal.ZERO);
                map.put("ticketNumsWork",BigDecimal.ZERO);
                map.put("yingfuMoneyWork",BigDecimal.ZERO);
            }

            //售出率：【售出票数÷（总规划票数-预留票数）】×100%
            Long seatNumsProgCollect = (Long)map.get("seatNumsProg");
//            BigDecimal sellNumsTotal = (BigDecimal)map.get("sellNumsTotal");
            Long seatNumsExpectCollect = (Long)map.get("seatNumsExpect");
            BigDecimal countTicketResCollect = (BigDecimal)map.get("countTicketRes");

            if(null != seatNumsExpectCollect && BigDecimal.valueOf(seatNumsExpectCollect).compareTo(countTicketResCollect)>0){
                BigDecimal sellsPercentCollect = BigDecimal.valueOf(seatNumsProgCollect).divide(BigDecimal.valueOf(seatNumsExpectCollect).subtract(countTicketResCollect),4,BigDecimal.ROUND_HALF_UP);
                map.put("sellsPercent",sellsPercentCollect.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
            }else{
                map.put("sellsPercent","0%");
            }

            if(null != seatNumsExpectCollect && seatNumsExpectCollect > 0){
                BigDecimal numsTotal = BigDecimal.valueOf(seatNumsProgCollect).add((BigDecimal)map.get("ticketNumsTuan"))
                        .add((BigDecimal)map.get("ticketNumsOther"))
                        .add((BigDecimal)map.get("ticketNumsZeng"))
                        .add((BigDecimal)map.get("ticketNumsWork"));
                //总体上座率：【总出票数（含普通票、团购票、赠票、工作票、其他票）÷总规划票数】×100%
                BigDecimal seatPercentCollect = numsTotal.divide(BigDecimal.valueOf(seatNumsExpectCollect),4,BigDecimal.ROUND_HALF_UP);
                map.put("seatPercent",seatPercentCollect.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
            }else{
                map.put("seatPercent","0%");
            }

            mapSessionMapCollect.put(map.get("sessionId")+"",map);
        }
        return mapSessionMapCollect;
    }

    /**
     * 明细查询拼接演出汇总
     * @return
     */
    private Map<String,Map<String,Object>> detailsCollectMapByPerformMake(String sessionIds){
        Map<String,Map<String,Object>> mapSessionProgCollect = Maps.newHashMap();
        Map<String,Map<String,Object>> mapSessionBackendCollect = Maps.newHashMap();
        Map<String,Map<String,Object>> mapSessionMapCollect = Maps.newHashMap();
        List<Map<String,Object>> listSessionCollectMap = yanchuStatisticsMapper.listDetailsByPerformTicketPricesCollect(sessionIds);
        List<Map<String,Object>> listDetailsCollectFromProg = yanchuStatisticsMapper.listDetailsCollectByPerformFromProg(sessionIds);
        List<Map<String,Object>> listDetailsCollectFromBackend = yanchuStatisticsMapper.listDetailsCollectByPerformFromBackend(sessionIds);

        if(CollectionUtils.isNotEmpty(listDetailsCollectFromProg)){
            for(Map<String,Object> map:listDetailsCollectFromProg){
                mapSessionProgCollect.put(map.get("performId")+"",map);
            }
        }
        if(CollectionUtils.isNotEmpty(listDetailsCollectFromBackend)){
            for(Map<String,Object> map:listDetailsCollectFromBackend){
                mapSessionBackendCollect.put(map.get("performId")+"",map);
            }
        }
        for(Map<String,Object> map:listSessionCollectMap){
            if(null != mapSessionProgCollect.get(map.get("performId")+"")){
                map.putAll(mapSessionProgCollect.get(map.get("performId")+""));
            }else{
                map.put("seatNumsProg",0l);
                map.put("yingfuMoneyProg",BigDecimal.ZERO);
                map.put("discountAmtProg",BigDecimal.ZERO);
                map.put("totalMoneyProg",BigDecimal.ZERO);
            }

            if(null != mapSessionBackendCollect.get(map.get("performId")+"")){
                map.putAll(mapSessionBackendCollect.get(map.get("performId")+""));
            }else{
                map.put("ticketNumsTuan",BigDecimal.ZERO);
                map.put("yingfuMoneyTuan",BigDecimal.ZERO);
                map.put("discountAmtTuan",BigDecimal.ZERO);
                map.put("totalMoneyTuan",BigDecimal.ZERO);
                map.put("shiShouTuan",BigDecimal.ZERO);
                map.put("ticketNumsOther",BigDecimal.ZERO);
                map.put("yingfuMoneyOther",BigDecimal.ZERO);
                map.put("discountAmtOther",BigDecimal.ZERO);
                map.put("totalMoneyOther",BigDecimal.ZERO);
                map.put("shiShouOther",BigDecimal.ZERO);
                map.put("ticketNumsZeng",BigDecimal.ZERO);
                map.put("yingfuMoneyZeng",BigDecimal.ZERO);
                map.put("ticketNumsWork",BigDecimal.ZERO);
                map.put("yingfuMoneyWork",BigDecimal.ZERO);
            }

            //售出率：【售出票数÷（总规划票数-预留票数）】×100%
            Long seatNumsProgCollect = (Long)map.get("seatNumsProg");
//            BigDecimal sellNumsTotal = (BigDecimal)map.get("sellNumsTotal");
            BigDecimal seatNumsExpectCollect = (BigDecimal)map.get("seatNumsExpect");
            BigDecimal countTicketResCollect = (BigDecimal)map.get("countTicketRes");

            if(null != seatNumsExpectCollect && seatNumsExpectCollect.compareTo(countTicketResCollect)>0){
                BigDecimal sellsPercentCollect = BigDecimal.valueOf(seatNumsProgCollect).divide(seatNumsExpectCollect.subtract(countTicketResCollect),4,BigDecimal.ROUND_HALF_UP);
                map.put("sellsPercent",sellsPercentCollect.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
            }else{
                map.put("sellsPercent","0%");
            }

            if(null != seatNumsExpectCollect){
                BigDecimal numsTotal = BigDecimal.valueOf(seatNumsProgCollect).add((BigDecimal)map.get("ticketNumsTuan"))
                        .add((BigDecimal)map.get("ticketNumsOther"))
                        .add((BigDecimal)map.get("ticketNumsZeng"))
                        .add((BigDecimal)map.get("ticketNumsWork"));
                //总体上座率：【总出票数（含普通票、团购票、赠票、工作票、其他票）÷总规划票数】×100%
                BigDecimal seatPercentCollect = numsTotal.divide(seatNumsExpectCollect,4,BigDecimal.ROUND_HALF_UP);
                map.put("seatPercent",seatPercentCollect.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP)+"%");
            }else{
                map.put("seatPercent","0%");
            }

            mapSessionMapCollect.put(map.get("performId")+"",map);
        }
        return mapSessionMapCollect;
    }


    /**
     * 结算统计
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public List<SettleGroupVO> getSettleGroupList(String startTime, String endTime,List<Long> deptIds ) {
        List<SettleGroupVO> settleGroupList = yanchuOrderMapper.getSettleGroupList(startTime, endTime,deptIds);
        settleGroupList.forEach(YanchuStatisticsServiceImpl::groupDataDProcessor);
        SettleGroupVO end = new SettleGroupVO();
        beanDataDProcessor(end,settleGroupList);
        settleGroupList.add(end);
        return settleGroupList;
    }



    /**
     * 结算明细
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public List<SettleGroupDetailVO> getSettleGroupDetail(String startTime, String endTime,List<Long> deptIds) {
        List<SettleGroupDetailVO> settleGroupList = yanchuOrderMapper.getSettleGroupDetail(startTime, endTime,deptIds);
        settleGroupList.forEach(YanchuStatisticsServiceImpl::groupDataDProcessor);
        SettleGroupDetailVO end = new SettleGroupDetailVO();
        beanDataDProcessor(end,settleGroupList);
        settleGroupList.add(end);
        return settleGroupList;
    }

    /**
     * 结算统计计算增强
     * @param settleGroupVO
     */
    private static void groupDataDProcessor(SettleGroupVO settleGroupVO) {
        settleGroupVO.setActualCardPay(settleGroupVO.getCardPay().subtract(settleGroupVO.getCardRefund()));
        settleGroupVO.setActualWechatPay(settleGroupVO.getWechatPay().subtract(settleGroupVO.getWechatRefund()));
        settleGroupVO.setActualBalancePay(settleGroupVO.getBalancePay().subtract(settleGroupVO.getBalanceRefund()));
        settleGroupVO.setActualAllPrice(settleGroupVO.getActualBalancePay().add(settleGroupVO.getActualWechatPay()).add(settleGroupVO.getActualCardPay()));
        settleGroupVO.setShouldPrice(settleGroupVO.getActualAllPrice().multiply(new BigDecimal("0.9")));
        settleGroupVO.setRefundPrice(settleGroupVO.getBalanceRefund().add(settleGroupVO.getCardRefund()).add(settleGroupVO.getWechatRefund()));
    }
    /**
     * 结算统计最后一行
     * @param
     */
    public static void beanDataDProcessor(SettleGroupVO end,List<? extends SettleGroupVO> settleGroupList){
        end.setOrderNum(settleGroupList.stream().mapToLong(SettleGroupVO::getOrderNum).sum());
        end.setDeptName("合计");
        end.setSaleNum(settleGroupList.stream().mapToLong(SettleGroupVO::getSaleNum).sum());
        end.setOrignPrice(settleGroupList.stream().map(SettleGroupVO::getOrignPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setDeducePrice(settleGroupList.stream().map(SettleGroupVO::getDeducePrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setYinfuPrice(settleGroupList.stream().map(SettleGroupVO::getYinfuPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setShifuPrice(settleGroupList.stream().map(SettleGroupVO::getShifuPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setRefundNum(settleGroupList.stream().mapToLong(SettleGroupVO::getRefundNum).sum());
        end.setRefundPrice(settleGroupList.stream().map(SettleGroupVO::getRefundPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setActualAllPrice(settleGroupList.stream().map(SettleGroupVO::getActualAllPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setBalancePay(settleGroupList.stream().map(SettleGroupVO::getBalancePay).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setCardPay(settleGroupList.stream().map(SettleGroupVO::getCardPay).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setWechatPay(settleGroupList.stream().map(SettleGroupVO::getWechatPay).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setBalanceRefund(settleGroupList.stream().map(SettleGroupVO::getBalanceRefund).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setCardRefund(settleGroupList.stream().map(SettleGroupVO::getCardRefund).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setWechatRefund(settleGroupList.stream().map(SettleGroupVO::getWechatRefund).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setShouldPrice(settleGroupList.stream().map(SettleGroupVO::getShouldPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setActualBalancePay(settleGroupList.stream().map(SettleGroupVO::getActualBalancePay).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setActualCardPay(settleGroupList.stream().map(SettleGroupVO::getActualCardPay).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setActualWechatPay(settleGroupList.stream().map(SettleGroupVO::getActualWechatPay).reduce(BigDecimal.ZERO, BigDecimal::add));
        end.setRates("10%");
    }


    @Override
    public void exportSettleGroupList(HttpServletResponse response, String startTime, String endTime,String exportMsg,List<Long> deptIds) {
        List<SettleGroupVO> settleGroupList = getSettleGroupList(startTime, endTime,deptIds);
        // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
        String templateFileName = tempUrl + "groupOrderTemp.xlsx";
        String fileName = "groupOrder"+System.currentTimeMillis()+".xlsx";
        try {
            //替换成自己的数据
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            ExcelWriterBuilder write = EasyExcel.write(response.getOutputStream());
            //只有一个直接写 >1的合并 分开写入
            try (ExcelWriter excelWriter = write.withTemplate(templateFileName).build()){
                WriteSheet writeSheet = EasyExcel.writerSheet().build();
                // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认 是false，会直接使用下一行，如果没有则创建。
                // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
                // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
                // 如果数据量大 list不是最后一行 参照下一个
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                excelWriter.fill(settleGroupList, fillConfig, writeSheet);
                Map<String, Object> map = MapUtils.newHashMap();
                String[] split = exportMsg.split("/n");
                StringBuilder builder = new StringBuilder();
                for (String s : split) {
                    builder.append(s).append("\n");
                }
                map.put("title", builder.toString());
                excelWriter.fill(map, writeSheet);
            }
        } catch (Exception e) {
            logger.error("exportSettleGroupList",e);
        }
    }

    @Override
    public void exportSettleGroupDetail(HttpServletResponse response, String startTime, String endTime,String exportMsg,List<Long> deptIds) {
        List<SettleGroupDetailVO> settleGroupDetailVOList = getSettleGroupDetail(startTime, endTime,deptIds);
        // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
        String templateFileName = tempUrl + "groupOrderDetailTemp.xlsx";
        String fileName = "groupOrderDetail"+System.currentTimeMillis()+".xlsx";
        try {
            //替换成自己的数据
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            ExcelWriterBuilder write = EasyExcel.write(response.getOutputStream());
            write.registerWriteHandler(new CustomMergeStrategy(settleGroupDetailVOList.stream().map(SettleGroupDetailVO::getDeptName).collect(Collectors.toList()), 0));
            //只有一个直接写 >1的合并 分开写入
            try (ExcelWriter excelWriter = write.withTemplate(templateFileName).build()){
                WriteSheet writeSheet = EasyExcel.writerSheet().build();
                // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认 是false，会直接使用下一行，如果没有则创建。
                // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
                // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
                // 如果数据量大 list不是最后一行 参照下一个
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                excelWriter.fill(settleGroupDetailVOList, fillConfig, writeSheet);
                Map<String, Object> map = MapUtils.newHashMap();
                String[] split = exportMsg.split("/n");
                StringBuilder builder = new StringBuilder();
                for (String s : split) {
                    builder.append(s).append("\n");
                }
                map.put("title", builder.toString());
                excelWriter.fill(map, writeSheet);
            }
        } catch (Exception e) {
            logger.error("exportSettleGroupDetail",e);
        }
    }


    @Override
    public List<MemberStatisticsVO> getMemberStatistics(String startTime, String endTime) {
        List<MemberStatisticsVO> memberStatistics = yanchuOrderMapper.getMemberStatistics(startTime, endTime);
        AtomicInteger index = new AtomicInteger();

        memberStatistics.forEach(memberStatisticsVO -> {
            memberStatisticsVO.setIndex(index.getAndIncrement()+1);
            if(memberStatisticsVO.getOrderNum()==0){
                memberStatisticsVO.setAverageConsume(new BigDecimal("0.00"));
            }else {
                memberStatisticsVO.setAverageConsume(memberStatisticsVO.getAllConsume().divide(BigDecimal.valueOf(memberStatisticsVO.getOrderNum()),2));
            }
            switch (memberStatisticsVO.getSex()){
                case "0":
                    memberStatisticsVO.setSex("男");
                    break;
                case "1":
                    memberStatisticsVO.setSex("女");
                    break;
                case "2":
                    memberStatisticsVO.setSex("未知");
                    break;
            }
            if(memberStatisticsVO.getIdNumber()!=null){
                memberStatisticsVO.setIdNumber(IdTypeEnum.indexOf(memberStatisticsVO.getIdType()).getValue()+":"+memberStatisticsVO.getIdNumber());
            }
            memberStatisticsVO.setAreaName(NationCache.getNationName(memberStatisticsVO.getAreaName()));
        });
        return memberStatistics;
    }




    @Override
    public void exportMemberStatistics(HttpServletResponse response, String startTime, String endTime, String exportMsg) {
        List<MemberStatisticsVO> memberStatistics = getMemberStatistics(startTime, endTime);
        // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
        String templateFileName = tempUrl + "memberStaticTemp.xlsx";
        String fileName = "memberStatic"+System.currentTimeMillis()+".xlsx";
        try {
            //替换成自己的数据
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
            ExcelWriterBuilder write = EasyExcel.write(response.getOutputStream());
            //只有一个直接写 >1的合并 分开写入
            try (ExcelWriter excelWriter = write.withTemplate(templateFileName).build()){
                WriteSheet writeSheet = EasyExcel.writerSheet().build();
                // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认 是false，会直接使用下一行，如果没有则创建。
                // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
                // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
                // 如果数据量大 list不是最后一行 参照下一个
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                excelWriter.fill(memberStatistics, fillConfig, writeSheet);
                Map<String, Object> map = MapUtils.newHashMap();
                String[] split = exportMsg.split("/n");
                StringBuilder builder = new StringBuilder();
                for (String s : split) {
                    builder.append(s).append("\n");
                }
                map.put("title", builder.toString());
                excelWriter.fill(map, writeSheet);
            }
        } catch (Exception e) {
            logger.error("exportMemberStatistics",e);
        }
    }
}
